from pyreadstat import pyreadstat
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np



def read_spss(filename):
 df0,metadata=pyreadstat.read_sav(filename,apply_value_formats=True)
 return df0
 

 
def print_all_cats(df):

    for col in df.columns[df.dtypes== 'category']:
     print(df[col].value_counts())


def print_all_int(df):

    for col in df.columns[df.dtypes== 'int']:
     print(df[col].describe())


def print_all_float(df):

    for col in df.columns[df.dtypes== 'float']:
     print(df[col].describe())





def gen_percent_table(df, col):
    """ 生成类别变量的频数频率表 """
    b = pd.DataFrame()
    b[F'{col}'] = df[F'{col}'].value_counts().index
    b['个数'] = df[F'{col}'].value_counts().values
    b['百分比'] = df[F'{col}'].value_counts(normalize=True).values * 100
    b['百分比'] = b['百分比'].apply(lambda x: round(x, 2))
    total_row = pd.Series({
        F"{col}": '总和',
        '个数': b['个数'].sum(),
        '百分比': b['百分比'].sum(),
    }).to_frame().T
    return pd.concat([b, total_row], ignore_index=True)





def show_bar(df, col, sort=True):


    set_plt()

    # 准备数据

    x = df[f'{col}'].value_counts(sort=sort).index
    y = df[f'{col}'].value_counts(sort=sort, normalize=True).values * 100

    # 绘图
    # 创建图
    fig, ax = plt.subplots()
    # 绘制柱状图
    rects1 = ax.bar(x, y)
    # 设定细节内容
    # 设置x轴变量名称
    ax.set_xlabel(f'{col}')
    # 设置y轴最大值
    ax.set_ylim(ymax=100)
    ax.bar_label(rects1, fmt="%.1f", padding=3)
    plt.show()



def set_plt():
    """ 图形参数设置 """
    # 设置字体
    plt.rcParams["font.sans-serif"] = ["SimHei"]
    # 该语句解决图像中的“-”负号的乱码问题
    plt.rcParams["axes.unicode_minus"] = False
    # 设置图形大小 大小的单位为英寸 inch
    plt.rcParams["figure.figsize"] = (6.4, 4.8)
    # 设置图形分辨率，出版物对图片的分辨率要求从150到300不等。
    plt.rcParams["figure.dpi"] = 200






def ordinal_desc(df, col: str = ''):
  
    # 初始化结果DataFrame
    result = pd.DataFrame()
    # 获得变量的内容
    result[f'{col}'] = df[col].value_counts(sort=False).index
    # 获得变量内容出现的次数
    result['个数'] = df[col].value_counts(sort=False).values
    # 获得各选项占比
    result['百分比'] = df[col].value_counts(normalize=True,
                                         sort=False).values * 100
    # 小数点后保留1位
    result['百分比'] = result['百分比'].apply(lambda x: round(x, 2))
    # 累计百分比
    result['累计百分比(%)'] = result['百分比'].values.cumsum()

    total_row = pd.Series({
        F"{col}": '总和',
        '个数': result['个数'].sum(),
        '百分比': result['百分比'].sum(),
        '累计百分比(%)': '',
    }).to_frame().T
    return pd.concat([result, total_row], ignore_index=True)





def goodmanKruska_tau_y(df, x: str, y: str) -> float:

    # 取得条件次数表
    cft = pd.crosstab(df[y], df[x], margins=True)
    # 取得全部个案数目
    n = cft.at['All', 'All']
    # 初始化变量
    E_1 = E_2 = tau_y = 0

    # 计算E_1
    for i in range(cft.shape[0] - 1):
        F_y = cft['All'][i]
        E_1 += ((n - F_y) * F_y) / n
    # 计算E_2
    for j in range(cft.shape[1] - 1):
        for k in range(cft.shape[0] - 1):
            F_x = cft.iloc[cft.shape[0] - 1, j]
            f = cft.iloc[k, j]
            E_2 += ((F_x - f) * f) / F_x
    # 计算tauy
    tau_y = (E_1 - E_2) / E_1

    return tau_y





def draw_on_corr(coefficient: int) -> str:

    result = ''
    if coefficient >= 0.8:
        result = '极强相关'
    elif coefficient >= 0.6:
        result = '强相关'
    elif coefficient >= 0.4:
        result = '中等相关'
    elif coefficient >= 0.2:
        result = '弱相关'
    else:
        result = '极弱相关或不相关'
    return result






def p_result(p: float) -> str:
    """
    根据接收到的p值,给出判断结果字典。

    参数：
    p: 各种检验得到的p值。

    返回值：
    以字典形式保存的判断结果。
    """
    if p >= 0.05:
        return {
            'p': f'p={round(p,3)}>0.05',
            'tex_p': 'p>0.05',
            'conclusion': '接收虚无假设，拒绝研究假设。',
        }
    elif p >= 0.01:
        return {
            'p': f'p={round(p,3)}<0.05',
            'tex_p': 'p<0.05',
            'conclusion': '拒绝虚无假设，接收研究假设。',
        }
    elif p >= 0.001:
        return {
            'p': f'p={round(p,3)}<0.01',
            'tex_p': 'p<0.01',
            'conclusion': '拒绝虚无假设，接收研究假设。',
        }
    else:
        return {
            'p': f'p={round(p,3)}<0.001',
            'tex_p': 'p<0.001',
            'conclusion': '拒绝虚无假设，接收研究假设。',
        }






def gen_scatter(df, x, y) -> str:


    set_plt()

    # 准备数据
    # print(df[x].dtype)
    if df[x].dtype == 'category':
        x1 = df[x].cat.codes
    else:
        x1 = df[x]
    if df[y].dtype == 'category':
        y1 = df[y].cat.codes
    else:
        y1 = df[y]

    # 绘图
    # 创建图
    fig, ax = plt.subplots()
    # 绘制散点图
    # print(x, y)
    art1 = ax.scatter(x1, y1)
    # 设定细节内容
    # 设置x轴变量名称
    ax.set_xlabel(f'{x}')
    # 设置y轴最大值
    ax.set_ylabel(f'{y}')
    plt.show()




def draw_on_r(coefficient: int) -> str:
    """根据coefficient的值给出建议内容"""
    result = ''
    if coefficient >= 0.56:
        result = '极强相关'
    elif coefficient >= 0.25:
        result = '中等'
    elif coefficient >= 0.06:
        result = '低度相关'
    else:
        result = '极弱相关或不相关'
    return result




def draw_on_eta2(coefficient: int) -> str:

    result = ''
    if coefficient >= 0.14:
        result = '高度相关'
    elif coefficient >= 0.06:
        result = '中度相关'
    elif coefficient >= 0.01:
        result = '低度相关'
    else:
        result = '极弱相关或不相关'
    return result




def to_sav(df, metadata, filename):

    pyreadstat.write_sav(df,
                         filename,
                         variable_value_labels=metadata.variable_value_labels,
                         variable_measure=metadata.variable_measure,
                         variable_format=metadata.original_variable_types)


def get_key(dct, value):
    """ 获取字典中指定值对应的第一个键 """
    result = [k for (k, v) in dct.items() if v == value]
    if result:
        return result[0]
    else:
        return None


def label_to_code(x, col, metadata):
    """ 获取指定值对应的编码值 """
    return get_key(metadata.variable_value_labels[col], x)


def set_label_to_code(df, metadata):
    """ 将SPSS文件中的文字标签转化为数值 """
    for col in df.columns[df.dtypes == 'category']:
        df[col] = df[col].apply(label_to_code, col=col, metadata=metadata)
    return df









def gen_mcq_df(df,x,pattern='┋'):
    """
    定义一个针对问卷星数据多选题的分析函数
    
    df: 数据框
    x: 问卷星中定义的多选题

    返回值： 一个包含所有选项及出现次数和所占比例的数据框

    如：

    |选项|次数|比例(%)|
    |--|--|--|
    |单纯根据个人喜爱|20|5.5|
    |广告内容较感兴趣|20|5.5|
    |喜欢的明星代言|20|5.5|
    |视频广告|20|5.5|
       
    """
    # 按照指定分隔符将多选题字符串转化包含多个选项的列表
    df['temp'] = df[x].str.split(pattern)
    # 初始化列表，用于保存所有多选题选项
    mcq_items = []
    # 循环所有个案，获取所有多选题选项
    for g in df['temp']:
        for label in g:
            # print(label)
            mcq_items.append(label)
    # 将多选题选项去重后转化为列表，方便构造dataframe
    result = list(set(mcq_items))
    # 构造包含选项、次数和比例的空表
    df_mcq_1 = pd.DataFrame(data=np.zeros([len(result), 2]),
                            index=result,
                            columns=['次数', '比例'])
    # 通过循环获取每个选项在多选题中累次出现的次数
    for i in df[x]:
        for label in result:
            if str(i).__contains__(label):
                df_mcq_1.loc[label, '次数'] += 1
    # 生成比例列
    df_mcq_1['比例'] = df_mcq_1['次数'] / df.shape[0] * 100

    return df_mcq_1.astype({'次数':"int"})